//
//  graph.cpp
//  cobalt
//
//  Created by Nicolas Godbout on 2013-08-28.
//  Copyright (c) 2013 Nicolas Godbout. All rights reserved.
//

#include "graph.h"
#include "../itertools.h"

namespace rdf {
    Graph&
    Graph::add(Triple const& tr) {
        if ( !tr )
            throw std::invalid_argument("Attempt at adding an empty Triple to a Graph.");
        if ( !tr.subject() || !tr.predicate() || !tr.object() )
            throw std::invalid_argument("Attempt at adding an empty Node to a Graph.");
        graph->sMap.insert(tr);
        graph->pMap.insert(tr);
        graph->oMap.insert(tr);
        graph->size++;
        return *this;
    }
    

    Graph&
    Graph::add(TripleList trs) {
        while ( !trs.empty() ) {
            add(trs.front());
            trs.pop_front();
        }
        return *this;
    }
    
    Graph
    Graph::match(RDFNode const& subject,
                 RDFNode const& predicate,
                 RDFNode const& object) const noexcept {
        
        Triple other ({subject, predicate, object}) ;
        other.setPattern();
        Graph ret{};
        
        if ( subject ) {
            GraphRecord::sMap_t const& sMap = graph->sMap;
            Range<triple_iterator> srange(sMap.equal_range(other));
            if ( predicate ) {
                GraphRecord::pMap_t const& pMap = graph->pMap;
                Range<triple_iterator> prange(pMap.equal_range(other));
                Intersect<Range<triple_iterator>,Range<triple_iterator>,Triple::lessT>
                sprange(srange,prange);
                if ( object ) {
                    GraphRecord::oMap_t const& oMap = graph->oMap;
                    Range<triple_iterator> orange(oMap.equal_range(other));
                    Intersect<Intersect<Range<triple_iterator>,Range<triple_iterator>,Triple::lessT>,Range<triple_iterator>,Triple::lessT>
                    sporange(sprange,orange);
                    while ( sporange ) {
                        ret.add(*sporange);
                        ++sporange;
                    }
                } else {
                    while ( sprange ) {
                        ret.add(*sprange);
                        ++sprange;
                    }
                }
            } else {
                while ( srange ) {
                    ret.add(*srange);
                    ++srange;
                }
            }
            return ret;
        }
        
        if ( predicate ) {
            GraphRecord::pMap_t const& pMap = graph->pMap;
            Range<triple_iterator> prange(pMap.equal_range(other));
            if ( object ) {
                GraphRecord::oMap_t const& oMap = graph->oMap;
                Range<triple_iterator> orange(oMap.equal_range(other));
                Intersect<Range<triple_iterator>,Range<triple_iterator>,Triple::lessT>
                porange(prange,orange);
                while ( porange ) {
                    ret.add(*porange);
                    ++porange;
                }
            } else {
                while ( prange ) {
                    ret.add(*prange);
                    ++prange;
                }
            }
            return ret;
        }
        
        if ( object ) {
            GraphRecord::oMap_t const& oMap = graph->oMap;
            Range<triple_iterator> orange = oMap.equal_range(other);
            while ( orange ) {
                ret.add(*orange);
                ++orange;
            }
            return ret;
        }
        
        return *this;
    }

    
}